package com.user.config;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class XiaoyuApplicationContext {


    //配置类
    private Class config;
    //初始的bean定义
    private Map<String,BeanDefinition> beanDefineMap = new HashMap<>();
    //单例缓存池
    private Map<String,Object> singleBean = new HashMap<>();

    public XiaoyuApplicationContext(Class myDemoConfigClass) {
        config = myDemoConfigClass;
        //解析配置类
        scan();

    }

    public void scan(){
        ComponentScan declaredAnnotation = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
        String value = declaredAnnotation.basePackages();
        doScan(value);
        //将bean定义Map生成具体的Bean对象
        beanDefineMap.entrySet().stream().forEach(item->{
            String beanName = item.getKey();
            BeanDefinition beanDefinition = item.getValue();
            if (!beanDefinition.isLazy() && "singleton".equals(beanDefinition.getScope())) {
                Object bean = createBean(beanName);
                singleBean.put(beanName,bean);
            }
        });
    }

    /**
     *     解析配置类
     */
    private void doScan(String value) {
        String path = value.replace(".","/");
        //正常走文件解析
        ClassLoader classLoader = this.getClass().getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());

        List<File> classFile = new ArrayList<>();
        //简单点直接双层解析即可
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                if (f.isDirectory()) {
                    for (File f1 : f.listFiles()) {
                        if (!f1.isDirectory()) {
                            classFile.add(f1);
                        }
                    }
                } else {
                    classFile.add(f);
                }
            }
        }
        //遍历所有解析文件
        for (File cFile : classFile) {
            String absolutePath = cFile.getAbsolutePath();
            String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                    .replace("\\", ".");

            try {
                Class<?> clazz = classLoader.loadClass(className);
                //是否需要被Spring管理
                if (clazz.isAnnotationPresent(Component.class)) {
                    //将bean上的注解封装到bean定义中
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setType(clazz);
                    beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));
                    if (clazz.isAnnotationPresent(Scope.class)) {
                        beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }

                    String beanName = clazz.getAnnotation(Component.class).value();
                    if (beanName.isEmpty()) {
                        //如果不设置beanName会默认生产唯一一个name，Spring底层也是这样做的
                        beanName = Introspector.decapitalize(clazz.getSimpleName());
                    }

                    beanDefineMap.put(beanName, beanDefinition);

                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

        }
    }

    public Object createBean(String beanName){

        BeanDefinition beanDefinition = beanDefineMap.get(beanName);
        Class type = beanDefinition.getType();

        try {
            Object instance = type.newInstance();
            //属性填充，依赖注入
            populateBean(instance);

            if (instance instanceof ApplicationContextAware){
                ((ApplicationContextAware) instance).setApplicationContext(this);
            }
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            //是否需要AOP增强
            if (type.isAnnotationPresent(Transaction.class)) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(type);
                //简单的方法切面
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        //开启事务，关闭自动提交
                        System.out.println("事务已开启");
                        Object res = method.invoke(instance, objects);
                        //提交事务
                        System.out.println("事务已提交");
                        return res;
                    }
                });
                return enhancer.create();
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    private void populateBean(Object instance) {
        Field[] declaredFields = instance.getClass().getDeclaredFields();

        Arrays.stream(declaredFields).forEach(item->{
            //寻找注入点
            if (item.isAnnotationPresent(AutoWired.class)) {
                Object bean = getBean(item.getName());
                item.setAccessible(true);
                try {
                    item.set(instance,bean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });

    }


    public Object getBean(String beanName){
        if (!beanDefineMap.containsKey(beanName)) {
           throw new NullPointerException();
        }
        if ("singleton".equals(beanDefineMap.get(beanName).getScope())) {
            if (singleBean.containsKey(beanName)) {
                return singleBean.get(beanName);
            } else {
                Object bean = createBean(beanName);
                singleBean.put(beanName,bean);
                return bean;
            }
        }
        return createBean(beanName);
    }
}
